from formencode import Invalid

from clutch.globals import G, C
from clutch.decorators import Anno
from clutch import exceptions as exc

class Controller(object):

    def _on_400(self, *args, **kwargs):
        '''Handle HTTP 400 (invalid) error codes'''
        return self._on_invalid(*args, **kwargs)

    def _on_invalid(self, *args, **kwargs):
        '''Handle Formencode Invalid exceptions'''
        controller = C.controller
        exc_info = C.exc_info
        anno = Anno.get(controller)
        eh = anno.error_handler
        if not eh:
            raise exc_info[0], exc_info[1], exc_info[2]
        d = dict(kwargs)
        d['_clutch_errors'] = C.validation_errors = exc_info[1].unpack_errors()
        d['_clutch_controller'] = controller
        d['_clutch_exc_info'] = exc_info
        # Replace the renderer
        new_self = controller.im_self
        C.controller = controller.im_func
        return eh(new_self, *args, **d)

    def _on_401(self, *args, **kwargs):
        '''Handle HTTP 401 (Unauthorized) exceptions'''
        controller = C.controller
        exc_info = C.exc_info
        location = G.config.login_url
        if location:
            raise exc.HTTPFound(location=location)
        else:
            raise exc_info[0], exc_info[1], exc_info[2]

    def _on_403(self, *args, **kwargs):
        '''Handle HTTP 403 (Forbidden) exceptions'''
        exc_info = C.exc_info
        raise exc_info[0], exc_info[1], exc_info[2]

    def _on_404(self, *args, **kwargs):
        '''Handle HTTP 404 (Not Found) exceptions'''
        exc_info = C.exc_info
        raise exc_info[0], exc_info[1], exc_info[2]

    def _on_error(self, *args, **kwargs):
        '''Dispatcher for all HTTP exceptions except 400,401,403,and 404'''
        exc_info = C.exc_info
        raise exc_info[0], exc_info[1], exc_info[2]

    def _on_exception(self, *args, **kwargs):
        '''Dispatcher for all non-HTTP exceptions'''
        exc_info = C.exc_info
        raise exc_info[0], exc_info[1], exc_info[2]

    def _error_handler(self, exc_info):
        '''Dispatcher for all exceptions'''
        if issubclass(exc_info[0], exc.HTTPError):
            handlers = {
                400: self._on_400,
                401: self._on_401,
                403: self._on_403,
                404: self._on_404 }
            return handlers.get(exc_info[0].code, self._on_error)
        elif issubclass(exc_info[0], Invalid):
            return self._on_invalid
        else:
            return self._on_exception

def redirect(url):
    raise exc.HTTPFound(location=url)

class RestMethod(object):

    class __metaclass__(type):

        def __new__(meta, name, bases, dct):
            cls = type.__new__(meta, name, bases, dct)
            if bases == (object,):
                return cls
            from clutch.wsgi import is_controller
            allowed_methods = {}
            for name, value in dct.iteritems():
                if is_controller(value, False):
                    allowed_methods[name] = value
            return _RestMethod(allowed_methods)

##    class __metaclass__(type):

##        def __new__(meta, name, bases, dct):
##            from clutch.wsgi import is_controller
##            cls = type.__new__(meta, name, bases, dct)
##            allowed_methods = cls.allowed_methods = {}
##            for name, value in dct.iteritems():
##                if is_controller(value, False):
##                    allowed_methods[name] = value
##            return cls

##        @property
##        def clutch_info(cls):
##            methodname = C.request.method.lower()
##            method = cls.allowed_methods.get(methodname)
##            if method is None:
##                raise exc.HTTPMethodNotAllowed()
##            return Anno.get(method)

##    def __call__(self, *l, **kw):
##        methodname = C.request.method.lower()
##        method = self.allowed_methods[methodname]
##        return method(self, *l, **kw)

class _RestMethod(object):

    def __init__(self, allowed_methods):
        self.allowed_methods = allowed_methods

    @property
    def clutch_info(self):
        methodname = C.request.method.lower()
        method = self.allowed_methods.get(methodname)
        if method is None:
            raise exc.HTTPMethodNotAllowed()
        else:
            return Anno.get(method)

    def __call__(self, *l, **kw):
        methodname = C.request.method.lower()
        method = self.allowed_methods.get(methodname)
        return method(self, *l, **kw)


class SecureResource(Controller): pass

